package org.yunai.yfserver.message;

import java.io.UnsupportedEncodingException;

public class ByteArray {
	private byte[] hb;
	private int position;
	private int limit;

	private ByteArray(int limit) {
		this.limit = limit;
	}
	
	private ByteArray(byte[] hb, int position) {
		this.hb = hb;
		this.position = position;
		this.limit = hb.length;
	}
	
	public static ByteArray createNull(int limit) {
		return new ByteArray(limit);
	}
	
	public void create() {
		this.hb = new byte[this.limit];
	}
	
	public void incrLimit(int incr) {
		this.limit += incr;
	}

	public static ByteArray wrap(byte[] hb) {
		return new ByteArray(hb, 0);
	}

	public byte[] getHb() {
		return hb;
	}

	public int position() {
		return position;
	}
	
	public int limit() {
		return limit;
	}
	
	public byte getByte() {
		return hb[position++];
	}

	public void putByte(byte[] bytes) {
		for (byte b : bytes) {
			hb[position++] = b;
		}
	}
	
	public void putByte(byte b) {
		hb[position++] = b;
	}
	
	public char getChar() {
		return Bits.getChar(hb, nextGetIndex(2));
	}

	public void putChar(char ch) {
		Bits.putChar(hb, nextGetIndex(2), ch);
	}

	public short getShort() {
		return Bits.getShort(hb, nextGetIndex(2));
	}

	public void putShort(short val) {
		Bits.putShort(hb, nextGetIndex(2), val);
	}

	public int getInt() {
		return Bits.getInt(hb, nextGetIndex(4));
	}

	public void putInt(int val) {
		Bits.putInt(hb, nextGetIndex(4), val);
	}

	public long getLong() {
		return Bits.getLong(hb, nextGetIndex(8));
	}

	public void putLong(long val) {
		Bits.putLong(hb, nextGetIndex(8), val);
	}

	public float getFloat() {
		return Bits.getFloat(hb, nextGetIndex(4));
	}

	public void putFloat(float val) {
		Bits.putFloat(hb, nextGetIndex(4), val);
	}

	public double getDouble() {
		return Bits.getDouble(hb, nextGetIndex(8));
	}

	public void putDouble(double val) {
		Bits.putDouble(hb, nextGetIndex(8), val);
	}

	public String getString(short len) {
		return Bits.getString(hb, nextGetIndex(len), len);
	}

    public byte[] convertString(String str) {
        byte[] bytes = Bits.convertString(str);
        this.incrLimit(bytes.length + 2);
        return bytes;
    }
	
	public int nextGetIndex(int nb) {
		int p = position;
		position += nb;
		return p;
	}
	
	public int nextGetIndex(short nb) {
		int p = position;
		position += nb;
		return p;
	}
	
	public void reset() {
		this.position = 0;
	}

    /*
    * Methods for unpacking primitive values from byte arrays starting at
    * given offsets.
    */
    private static class Bits {

        static boolean getBoolean(byte[] b, int off) {
            return b[off] != 0;
        }

        static char getChar(byte[] b, int off) {
            return (char) (((b[off + 1] & 0xFF)) +
                    ((b[off]) << 8));
        }

        static short getShort(byte[] b, int off) {
            return (short) (((b[off + 1] & 0xFF)) +
                    ((b[off]) << 8));
        }

        static int getInt(byte[] b, int off) {
            return ((b[off + 3] & 0xFF)) +
                    ((b[off + 2] & 0xFF) << 8) +
                    ((b[off + 1] & 0xFF) << 16) +
                    ((b[off]) << 24);
        }

        static float getFloat(byte[] b, int off) {
            int i = ((b[off + 3] & 0xFF)) +
                    ((b[off + 2] & 0xFF) << 8) +
                    ((b[off + 1] & 0xFF) << 16) +
                    ((b[off]) << 24);
            return Float.intBitsToFloat(i);
        }

        static long getLong(byte[] b, int off) {
            return ((b[off + 7] & 0xFFL)) +
                    ((b[off + 6] & 0xFFL) << 8) +
                    ((b[off + 5] & 0xFFL) << 16) +
                    ((b[off + 4] & 0xFFL) << 24) +
                    ((b[off + 3] & 0xFFL) << 32) +
                    ((b[off + 2] & 0xFFL) << 40) +
                    ((b[off + 1] & 0xFFL) << 48) +
                    (((long) b[off]) << 56);
        }

        static double getDouble(byte[] b, int off) {
            long j = ((b[off + 7] & 0xFFL)) +
                    ((b[off + 6] & 0xFFL) << 8) +
                    ((b[off + 5] & 0xFFL) << 16) +
                    ((b[off + 4] & 0xFFL) << 24) +
                    ((b[off + 3] & 0xFFL) << 32) +
                    ((b[off + 2] & 0xFFL) << 40) +
                    ((b[off + 1] & 0xFFL) << 48) +
                    (((long) b[off]) << 56);
            return Double.longBitsToDouble(j);
        }

        static String getString(byte[] b, int off, short len) {
            byte[] strBytes = new byte[len];
            System.arraycopy(b, off, strBytes, 0, len);
            return new String(strBytes);
        }

        /*
        * Methods for packing primitive values into byte arrays starting at given
        * offsets.
        */
        static void putBoolean(byte[] b, int off, boolean val) {
            b[off] = (byte) (val ? 1 : 0);
        }

        static void putChar(byte[] b, int off, char val) {
            b[off + 1] = (byte) (val);
            b[off] = (byte) (val >>> 8);
        }

        static void putShort(byte[] b, int off, short val) {
            b[off + 1] = (byte) (val);
            b[off] = (byte) (val >>> 8);
        }

        static void putInt(byte[] b, int off, int val) {
            b[off + 3] = (byte) (val);
            b[off + 2] = (byte) (val >>> 8);
            b[off + 1] = (byte) (val >>> 16);
            b[off] = (byte) (val >>> 24);
        }

        static void putFloat(byte[] b, int off, float val) {
            int i = Float.floatToIntBits(val);
            b[off + 3] = (byte) (i);
            b[off + 2] = (byte) (i >>> 8);
            b[off + 1] = (byte) (i >>> 16);
            b[off] = (byte) (i >>> 24);
        }

        static void putLong(byte[] b, int off, long val) {
            b[off + 7] = (byte) (val);
            b[off + 6] = (byte) (val >>> 8);
            b[off + 5] = (byte) (val >>> 16);
            b[off + 4] = (byte) (val >>> 24);
            b[off + 3] = (byte) (val >>> 32);
            b[off + 2] = (byte) (val >>> 40);
            b[off + 1] = (byte) (val >>> 48);
            b[off] = (byte) (val >>> 56);
        }

        static void putDouble(byte[] b, int off, double val) {
            long j = Double.doubleToLongBits(val);
            b[off + 7] = (byte) (j);
            b[off + 6] = (byte) (j >>> 8);
            b[off + 5] = (byte) (j >>> 16);
            b[off + 4] = (byte) (j >>> 24);
            b[off + 3] = (byte) (j >>> 32);
            b[off + 2] = (byte) (j >>> 40);
            b[off + 1] = (byte) (j >>> 48);
            b[off] = (byte) (j >>> 56);
        }

        static byte[] convertString(String str) {
            try {
                return str.getBytes("utf8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        }

        static void putString(byte[] b, int off, byte[] strBytes) {
            for (int i = 0, len = strBytes.length; i < len; i++) {
                b[off + i] = strBytes[i];
            }
        }
    }
}
